#include "../include/LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>

using namespace std;

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
    m_ls(nomFich), m_ts()
{
}

void LecteurPhraseAvecArbre::analyse()
{
    this->m_arbre = programme();
    cout << "Syntaxe correcte." << endl;
}

Noeud* LecteurPhraseAvecArbre::programme()
{
    // <programme> ::= debut <seq_inst> fin FIN_FICHIER
    sauterSymCour("debut");
    Noeud* si = seqInst();
    sauterSymCour("fin");
    testerSymCour("<FINDEFICHIER>");
    return si;
}

Noeud* LecteurPhraseAvecArbre::seqInst()
{
    // <seqInst> ::= <inst> ; { <inst> ; }
    NoeudSeqInst* si = new NoeudSeqInst();
    do
    {
        si->ajouteInstruction(inst());
        sauterSymCour(";");
    }
    while(this->m_ls.getSymCour() == "<VARIABLE>");      // tant que le symbole courant est un debut possible d'instruction...
    return si;
}

Noeud* LecteurPhraseAvecArbre::inst()
{
    // <inst> ::= <affectation> | <si> | <tantque> | <repeter> | <inst_condi>
    /*
    if(this->m_ls.getSymCour() == "si")
    {
        return instSi();
    }
    else if(this->m_ls.getSymCour() == "tantque")
    {
        return instTq();
    }
    else if(this->m_ls.getSymCour() == "repeter")
    {
        return instRepeter();
    }
    else if(this->m_ls.getSymCour() == "lire")
    {
        return instLire();
    }
    else if(this->m_ls.getSymCour() == "ecrire")
    {
        return instEcrire();
    }
    else if(this->m_ls.getSymCour() == "pour")
    {
        return instPour();
    }
    else
    {
    */
        return affectation();
    //}
}

Noeud* LecteurPhraseAvecArbre::affectation()
{
    // <affectation> ::= <variable> = <expression>
    testerSymCour("<VARIABLE>");
    Noeud* var = this->m_ts.chercheAjoute(this->m_ls.getSymCour());
    this->m_ls.suivant();
    sauterSymCour("=");
    Noeud* exp = expression();
    return new NoeudAffectation(var, exp);
}

Noeud* LecteurPhraseAvecArbre::expression()
{
    // <expression> ::= <facteur> { <opBinaire> <facteur> }
    Noeud* fact = facteur();
    while(this->m_ls.getSymCour() == "+" || this->m_ls.getSymCour() == "-")
    {
        Symbole operateur = opAdd();
        Noeud* factDroit = facteur(); // lecture de l'operande droit
        fact = new NoeudOperateurBinaire(operateur, fact, factDroit);    // const. du noeud
    }
    return fact;
}

Noeud* LecteurPhraseAvecArbre::facteur()
{
    // <facteur> ::= <entier>  |  <variable>  |  - <facteur>  |  ( <expression> )
    Noeud* fact = NULL;
    if(this->m_ls.getSymCour() == "<VARIABLE>" || this->m_ls.getSymCour() == "<ENTIER>")
    {
        fact = this->m_ts.chercheAjoute(this->m_ls.getSymCour());
        this->m_ls.suivant();
    }
    else if(this->m_ls.getSymCour() == "-")
    {
        this->m_ls.suivant();
        // on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
        fact = new NoeudOperateurBinaire(Symbole("-"), this->m_ts.chercheAjoute(Symbole("0")), facteur());
    }
    else if(this->m_ls.getSymCour() == "(")
    {
        this->m_ls.suivant();
        fact = expression();
        sauterSymCour(")");
    }
    else
    {
        erreur("<facteur>");
    }
    return fact;
}

Symbole LecteurPhraseAvecArbre::opAdd()
{
    // <opAdd>  ::= + | -
    Symbole operateur;
    if(this->m_ls.getSymCour() == "+" || this->m_ls.getSymCour() == "-")
    {
        operateur = this->m_ls.getSymCour();
        this->m_ls.suivant();
    }
    else
    {
        erreur("<opAdd>");
    }
    return operateur;
}

Symbole LecteurPhraseAvecArbre::opMult()
{
    // <opMult>  ::= *  | /
    Symbole operateur;
    if(this->m_ls.getSymCour() == "*" || this->m_ls.getSymCour() == "/")
    {
        operateur = this->m_ls.getSymCour();
        m_ls.suivant();
    }
    else
    {
        erreur("<opMult>");
    }
    return operateur;
}

Symbole LecteurPhraseAvecArbre::opBool()
{
    // <opBool> ::=  "et" | "ou"
    Symbole operateur;
    if(this->m_ls.getSymCour() == "et" || this->m_ls.getSymCour() == "ou")
    {
        operateur = this->m_ls.getSymCour();
        this->m_ls.suivant();
    }
    else
    {
        erreur("<opBool>");
    }
    return operateur;
}

Symbole LecteurPhraseAvecArbre::opRel()
{
    // <opRel>  ::= <  | <= | > | >= | == | !=
    Symbole operateur;
    if(this->m_ls.getSymCour() == "<"   || this->m_ls.getSymCour() == "<=" ||
            this->m_ls.getSymCour() == ">"   || this->m_ls.getSymCour() == ">=" ||
            this->m_ls.getSymCour() == "==" || this->m_ls.getSymCour() == "!=")
    {
        operateur = this->m_ls.getSymCour();
        this->m_ls.suivant();
    }
    else
    {
        erreur("<opRel>");
    }
    return operateur;
}

Symbole LecteurPhraseAvecArbre::opUnaire()
{
    // <opUnaire> ::= - | non
    Symbole operateur;
    if(this->m_ls.getSymCour() == "-" || this->m_ls.getSymCour() == "non")
    {
        operateur = this->m_ls.getSymCour();
        this->m_ls.suivant();
    }
    else
    {
        erreur("<opUnaire>");
    }
    return operateur;
}
/*
Noeud* LecteurPhraseAvecArbre::affectation()
{
    // <affectation> ::= <variable> = <expression>
    testerSymCour("<VARIABLE>");
    Noeud* var = this->m_ts.chercheAjoute(this->m_ls.getSymCour());
    this->m_ls.suivant();
    sauterSymCour("=");
    Noeud* exp = expression();
    return new NoeudAffectation(var, exp);
}

Noeud* LecteurPhraseAvecArbre::instLire()
{
    Noeud* var;
    Noeud* exp;
    sauterSymCour("lire");
    sauterSymCour("(");
    if(this->m_ls.getSymCour() == "<VARIABLE>")
    {
        var = this->m_ts.chercheAjoute(this->m_ls.getSymCour());
        exp = facteur();
        sauterSymCour(")");
    }
    else
    {
        erreur("<variable>");
    }
    return new NoeudInstLire(var, exp);
}

Noeud* LecteurPhraseAvecArbre::instEcrire() //Zfzf
{
    // <instEcrire> ::= ecrire ( <expression> | <chaine> )
    sauterSymCour("ecrire");
    sauterSymCour("(");
    if(this->m_ls.getSymCour() == "<CHAINE>")
    {
        this->m_ts.chercheAjoute(this->m_ls.getSymCour());
        this->m_ls.suivant();
    }
    else
    {
        expression();
    }
    sauterSymCour(")");
}

Noeud* LecteurPhraseAvecArbre::instSi()
{
    if(this->m_ls.getSymCour() == "si")
    {
        sauterSymCour("si");
        sauterSymCour("(");
        expBool();
        sauterSymCour(")");
        seqInst();
        while(this->m_ls.getSymCour() == "sinonsi")
        {
            sauterSymCour("sinonsi");
            sauterSymCour("(");
            expBool();
            sauterSymCour(")");
            seqInst();
        }
        if(this->m_ls.getSymCour() == "sinon")
        {
            sauterSymCour("sinon");
            seqInst();
        }
        sauterSymCour("finsi");
    }
}

Noeud* LecteurPhraseAvecArbre::instTq()
{
    if(this->m_ls.getSymCour() == "tantque")
    {
        sauterSymCour("tantque");
        sauterSymCour("(");
        expBool();
        sauterSymCour(")");
        seqInst();
        sauterSymCour("fintantque");
    }
}

Noeud* LecteurPhraseAvecArbre::instRepeter()
{
    if(this->m_ls.getSymCour() == "repeter")
    {
        sauterSymCour("repeter");
        seqInst();
        sauterSymCour("jusqua");
        sauterSymCour("(");
        expBool();
        sauterSymCour(")");
    }
}

Noeud* LecteurPhraseAvecArbre::instPour() //gerer les erreurs
{
    // <instPour> ::= pour (<affecation>; <expBool>; <affectation>;)
    if(this->m_ls.getSymCour() == "pour")
    {
        sauterSymCour("pour");
        sauterSymCour("(");
        affectation();
        sauterSymCour(";");
        expBool();
        sauterSymCour(";");
        affectation();
        sauterSymCour(")");
        seqInst();
        sauterSymCour("finpour");
    }
}
*/

void LecteurPhraseAvecArbre::testerSymCour(string ch)
{
    if(this->m_ls.getSymCour() != ch)
    {
        cout << endl << "-------- Erreur ligne " << this->m_ls.getLigne()
             << " - Colonne " << this->m_ls.getColonne() << endl << "   Attendu : "
             << ch << endl << "   Trouve  : " << this->m_ls.getSymCour() << endl << endl;
        exit(0);    // plus tard, on levera une exception
    }
}

void LecteurPhraseAvecArbre::sauterSymCour(string ch)
{
    testerSymCour(ch);
    this->m_ls.suivant();
}

void LecteurPhraseAvecArbre::erreur(string mess)
{
    cout << endl << "-------- Erreur ligne " << this->m_ls.getLigne() << " - Colonne "
         << this->m_ls.getColonne() << endl << "   Attendu : " << mess << endl
         << "   Trouve  : " << this->m_ls.getSymCour() << endl << endl;
    exit(0);    // plus tard, on levera une exception
}
